package com.longtop.sp.mapdao.dialect.oracle;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;

import com.longtop.model.Config;
import com.longtop.sp.mapdao.Lob;
import com.longtop.sp.mapdao.PrimaryKeyHelper;
import com.longtop.sp.mapdao.dialect.ConfigProperties;
import com.longtop.sp.mapdao.impl.AbstractInsert;
import com.longtop.sp.mapdao.meta.IPrimaryKey;
import com.longtop.sp.mapdao.meta.TableMetaData;
import com.longtop.sp.mapdao.meta.TableMetaDataPool;

/**
 */
public class OracleInsert extends AbstractInsert {
	
	private PrimaryKeyHelper pkHelper;

	public PrimaryKeyHelper getPkHelper() {
		return pkHelper;
	}

	/**
	 * @todo 因为时间紧迫，目前仍然是采用FieldEntryList，进行计算交集、格式化字符串，今后可以考虑弄一个formatter，
	 *       甚至根据数据库数据类型和JDBC数据类型，弄一个对应关系表，能够自动进行格式化。
	 */
	private static Log log = LogFactory.getLog(OracleInsert.class);

	// 用于执行的sql语句，将它作为成员变量，便于在出错时打印在log日志中
	private String sql;

	public OracleInsert() {
	}

	public int executeImpl() throws Exception {
		try {
			return executeImplWithException();
		} catch (SQLException ex) {
			log.warn("数据库组件OracleInsert在插入数据时发生SQLException异常！sql=" + sql);
			throw ex;
		} catch (IOException ex) {
			log.warn("数据库组件OracleInsert在插入数据时发生IOException异常！sql=" + sql);
			throw ex;
		}
	}

	public int executeImplWithException() throws Exception {
		// 获取主键
		IPrimaryKey pk = this.pkFactory.createPrimarykey(jdbcTemplate,
				tableName,ConfigProperties.getValue());
		TableMetaData rsmt = TableMetaDataPool.getMetaData(jdbcTemplate,
				tableName);
		pkHelper.reBuildDataMap(pk, tmData);
		// 获取表的字段和Map数据的交集，以便准备sql语句，也就是需要插入的数据
		FieldEntryList fields = new FieldEntryList(rsmt, tmData);
		Map<String, Object> pks = pkHelper.getPrimaryKey(pk, fields);
		for (Iterator iterator = pks.keySet().iterator(); iterator.hasNext();) {
			String key = (String) iterator.next();
			Object value = pks.get(key);
			tmData.put(key, value);
		}
		fields = new FieldEntryList(rsmt, tmData);
		String pkAndValue = pk.getPrimaryKeyAndValue(tmData, rsmt);
		sql = buildSql(fields);
		return internalExecute(fields, pkAndValue);

	}

	private int internalExecute(FieldEntryList fields, String pkAndValue)
			throws Exception {
		Connection conn = DataSourceUtils.getConnection(jdbcTemplate
				.getDataSource());
		Statement stmt = null;
		ResultSet rs = null;
		int insertRowCount = 0;
		try {

			// 如果有lob字段，而且本来AutoCommit就是true，设为false
			boolean oldAutoCommit = conn.getAutoCommit();
			if (oldAutoCommit && fields.isHaveLob() && "oracle".equals(ConfigProperties.getValue())) {
				conn.setAutoCommit(false);
			}
			stmt = conn.createStatement();
			insertRowCount = stmt.executeUpdate(sql);
			StringBuffer sqlBuffer = null;
			// 处理lob
			if (fields.isHaveLob() && "oracle".equals(ConfigProperties.getValue())) {
				// 将刚刚插入的纪录用 Select * from tab where ... for update定位
				sqlBuffer = new StringBuffer("SELECT ");
				boolean addComma = false;
				for (int i = 0, m = fields.getColumnCount(); i < m; i++) {
					if (fields.isLob(i)) {
						if (addComma) {
							sqlBuffer.append(",");
						}
						sqlBuffer.append(fields.getFieldName(i));
						addComma = true;
					}
				}
				sqlBuffer.append(" FROM " + tableName + " WHERE " + pkAndValue
						+ " FOR UPDATE");
				sql = new String(sqlBuffer);
				if (log.isDebugEnabled()) {
					log.debug("重新定位包含lob的纪录，准备处理lob字段sql=" + sql);
				}
				rs = stmt.executeQuery(sql);
				if (!rs.next()) {
					String msg = "未知错误，不能定位到刚刚插入或更新的纪录上。定位包含lob的纪录sql=" + sql;
					throw new SQLException(msg);
				}
				// 开始向LOB中写入数据
				Lob lob = new OracleLob();
				for (int i = 0; i < fields.getColumnCount(); i++) {
					if (fields.isBlob(i)) { // BLOB
						String f = fields.getFieldName(i);
						lob.writeBLOB(rs, f, tmData.get(f));
					} else if (fields.isClob(i)) { // CLOB
						String f = fields.getFieldName(i);
						lob.writeCLOB(rs, f, tmData.get(f));
					}
				}
			}

			// 处理提交问题，如果原本就是autoCommit，且因为有LOB而导致当前为不自动提交，就应该在这里提交
			if (oldAutoCommit && fields.isHaveLob()) {
				conn.commit();
			}
		} catch (Exception e) {
			throw e;
		} finally {
			JdbcUtils.closeResultSet(rs);
			JdbcUtils.closeStatement(stmt);

			DataSourceUtils.releaseConnection(conn, jdbcTemplate
					.getDataSource());
		}

		return insertRowCount;
	}

	private String buildSql(FieldEntryList fields) throws Exception {

		// 没有发现可以插入的字段，不需要执行，抛出异常
		if (fields.getColumnCount() == 0) {
			throw new Exception("Map中的数据与数据库表的字段名均不相同，没有发现可以插入的字段。");
		}

		// 构造一个insert into tableName(field1,...) values(... ...)语句
		StringBuffer sqlBuffer = new StringBuffer();
		sqlBuffer.append("INSERT INTO " + tableName + " (");
		for (int i = 0; i < fields.getColumnCount(); i++) {
			if (i != 0) {
				sqlBuffer.append(",");
			}
			sqlBuffer.append(fields.getFieldName(i));
		}
		sqlBuffer.append(") VALUES(");
		for (int i = 0; i < fields.getColumnCount(); i++) {
			if (i != 0) {
				sqlBuffer.append(",");
			}
			if(fields.getFieldValue(i).indexOf("POINT(") != -1 && "postgresql".equals(ConfigProperties.getValue())){
				sqlBuffer.append("(select st_geometryfromtext('" + fields.getFieldValue(i) + "',4326))");
			}else{
				if("ARRAY".equals(fields.getFieldType(i))){
					sqlBuffer.append("array["+fields.getFieldValue(i).substring(1, fields.getFieldValue(i).length()-1)+"]");
				}else{
					sqlBuffer.append(fields.getFieldValue(i));
				}
			}
			
		}
		sqlBuffer.append(")");

		sql = new String(sqlBuffer);
		if (log.isDebugEnabled()) {
			log.debug("插入的sql语句=" + sql);
		}
		return sql;
	}

	public void setPkHelper(PrimaryKeyHelper pkHelper) {
		this.pkHelper = pkHelper;
	}
}